#!/usr/bin/env python3  /*设定脚本文件直接能执行*/
# -*- coding:utf-8 -*-   /*设置编码*/
"""
@Author: zhuo
@Software: PyCharm
@File: tQParaSetForm.py
@Time: 2024/1/24 14:52
@Function：ctQParaSetForm：自定义电机参数设置类
"""
import os
import time
from datetime import datetime

from PySide6.QtCore import Qt
from PySide6.QtWidgets import *

from tBLL import tExcelHelper
from tModels.tParaInfo import ctParaInfo
from tModels.tThread import ctMultiRWThread
from tUI.tQGroupBox import ctQGroupBox


class ctQParaSetForm(QHBoxLayout):
    """
    自定义电机参数设置类
    """

    # 初始化
    def __init__(self, widget, rtu, control, workbench):
        # 继承自父类
        super(ctQParaSetForm, self).__init__()
        # 所在的widget
        self.paraSetTab = widget
        # 参数信息
        self.para = ctParaInfo()
        # 传入的通讯类实例
        self.rtu = rtu
        # 传入的控制台实例
        self.control = control
        # 传入的工作台示例
        self.workbench = workbench
        # 多个参数读写的字典
        self.dic = {}
        # 多个参数读写线程
        self.multiThread = ctMultiRWThread(self.rtu)
        self.multiThread.writeSignal.connect(self.__f_writeUpdateUI)
        self.multiThread.readSignal.connect(self.__f_readUpdateUI)
        self.multiThread.dialogUpdated.connect(self.__f_dialogChangedUI)
        # 多个参数读写线程停止 发送的停止信号连接槽函数
        self.multiThread.stopSignal.connect(lambda: self.__f_btnSetEnabled(True))
        # 调用控制界面布局函数
        self.__f_paraSetLayout()

    # <editor-fold desc="布局函数">
    # 参数设置界面布局函数
    def __f_paraSetLayout(self):
        """
        function:  参数设置界面布局函数
              in:  None
             out:  None
          return:  None
          others:  Parameter Setting Form Layout Func
        """
        # 总体布局:：垂直布局
        self.mainLayout = QVBoxLayout()
        # 参数一键读取&写入布局
        btnLayout = QHBoxLayout()
        # 创建QTabWidget()
        self.tab = QTabWidget()
        # QTabWidget增加Tab页
        self.tabOne = QWidget()
        self.tabTwo = QWidget()
        self.tabThree = QWidget()
        # 设置ObjectName，可有可没有
        self.tabOne.setObjectName("tabOne")
        self.tab.addTab(self.tabOne, "电机本体参数")
        self.tab.addTab(self.tabTwo, "电机控制参数")
        self.tab.addTab(self.tabThree, "电机保护参数")
        # <editor-fold desc="电机参数一键读取&写入布局">
        self.btnAllRead = QPushButton("一键读取")
        self.btnAllRead.clicked.connect(lambda: self.__f_btnAllRWClicked(False))
        self.btnAllRead.setFixedWidth(70)
        self.btnAllWrite = QPushButton("一键写入")
        self.btnAllWrite.setFixedWidth(70)
        self.btnAllWrite.clicked.connect(lambda: self.__f_btnAllRWClicked(True))
        self.btnImport = QPushButton("一键导入")
        self.btnImport.clicked.connect(self.__f_btnImportClicked)
        self.btnImport.setFixedWidth(70)
        self.btnExport = QPushButton("一键导出")
        self.btnExport.clicked.connect(self.__f_btnExportClicked)
        self.btnExport.setFixedWidth(70)
        lbl = QLabel("电机参数   :  ")
        spacerH = QSpacerItem(1, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)
        btnLayout.addWidget(lbl)
        btnLayout.addWidget(self.btnAllRead)
        btnLayout.addWidget(self.btnAllWrite)
        # "|" 进行分割
        btnLayout.addWidget(QLabel("|"))
        btnLayout.addWidget(self.btnImport)
        btnLayout.addWidget(self.btnExport)
        btnLayout.addItem(spacerH)
        # </editor-fold>
        self.mainLayout.addLayout(btnLayout)
        # 布局中添加TabWidget()
        self.mainLayout.addWidget(self.tab)

        # <editor-fold desc="选中写入&全部写入&全部读取按键">
        # 电机本体参数
        self.btnMotorPraSelectedWrite = QPushButton("选中写入")
        self.btnMotorPraSelectedWrite.setFixedSize(120, 25)
        self.btnMotorPraSelectedWrite.clicked.connect(lambda:
                                                      self.__f_multiBtnRWClicked(True, True, True,
                                                                                 self.para.motorPraDic))
        self.btnMotorPraAllWrite = QPushButton("全部写入")
        self.btnMotorPraAllWrite.setFixedSize(120, 25)
        self.btnMotorPraAllWrite.clicked.connect(lambda:
                                                 self.__f_multiBtnRWClicked(False, True, True,
                                                                            self.para.motorPraDic))
        self.btnMotorPraAllRead = QPushButton("全部读取")
        self.btnMotorPraAllRead.setFixedSize(120, 25)
        self.btnMotorPraAllRead.clicked.connect(lambda:
                                                self.__f_multiBtnRWClicked(False, False, True,
                                                                           self.para.motorPraDic))
        # 电机控制参数
        self.btnMotorControlSelectedWrite = QPushButton("选中写入")
        self.btnMotorControlSelectedWrite.setFixedSize(120, 25)
        self.btnMotorControlSelectedWrite.clicked.connect(lambda:
                                                          self.__f_multiBtnRWClicked(True, True, False,
                                                                                     self.para.motorControlDic))
        self.btnMotorControlAllWrite = QPushButton("全部写入")
        self.btnMotorControlAllWrite.setFixedSize(120, 25)
        self.btnMotorControlAllWrite.clicked.connect(lambda:
                                                     self.__f_multiBtnRWClicked(False, True, False,
                                                                                self.para.motorControlDic))
        self.btnMotorControlAllRead = QPushButton("全部读取")
        self.btnMotorControlAllRead.setFixedSize(120, 25)
        self.btnMotorControlAllRead.clicked.connect(lambda:
                                                    self.__f_multiBtnRWClicked(False, False, False,
                                                                               self.para.motorControlDic))
        # 电机保护参数
        self.btnMotorProtectSelectedWrite = QPushButton("选中写入")
        self.btnMotorProtectSelectedWrite.setFixedSize(120, 25)
        self.btnMotorProtectSelectedWrite.clicked.connect(lambda:
                                                          self.__f_multiBtnRWClicked(True, True, False,
                                                                                     self.para.motorProtectDic))
        self.btnMotorProtectAllWrite = QPushButton("全部写入")
        self.btnMotorProtectAllWrite.setFixedSize(120, 25)
        self.btnMotorProtectAllWrite.clicked.connect(lambda:
                                                     self.__f_multiBtnRWClicked(False, True, False,
                                                                                self.para.motorProtectDic))
        self.btnMotorProtectAllRead = QPushButton("全部读取")
        self.btnMotorProtectAllRead.setFixedSize(120, 25)
        self.btnMotorProtectAllRead.clicked.connect(lambda:
                                                    self.__f_multiBtnRWClicked(False, False, False,
                                                                               self.para.motorProtectDic))
        # </editor-fold>

        # 调用电机本体参数布局函数
        self.__f_bodyParaLayout()

        # <editor-fold desc="电机控制参数布局">
        self.tabThreeLayout = QHBoxLayout()

        tabThreeLeftLayout = QVBoxLayout()
        tabThreeMiddleLayout = QVBoxLayout()
        tabThreeRightLayout = QVBoxLayout()
        self.tabThreeLayout.addLayout(tabThreeLeftLayout)
        self.tabThreeLayout.addLayout(tabThreeMiddleLayout)
        self.tabThreeLayout.addLayout(tabThreeRightLayout)

        self.tabTwo.setLayout(self.tabThreeLayout)

        # 水平弹簧
        spacerH = QSpacerItem(1, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)
        spacerV = QSpacerItem(20, 10, QSizePolicy.Preferred, QSizePolicy.Expanding)

        self.__f_groupLayout(self.para.motorControlDic, tabThreeLeftLayout, " ", 220)
        self.__f_groupLayout(self.para.motorControlDic, tabThreeLeftLayout, "预充电功能", 220)
        self.__f_groupLayout(self.para.motorControlDic, tabThreeLeftLayout, "预定位参数", 220)
        self.__f_groupLayout(self.para.motorControlDic, tabThreeLeftLayout, "电机启动参数", 220)

        self.__f_groupLayout(self.para.motorControlDic, tabThreeMiddleLayout, "外环控制参数", 140)
        self.__f_groupLayout(self.para.motorControlDic, tabThreeMiddleLayout, "弱磁功能", 140)
        self.__f_groupLayout(self.para.motorControlDic, tabThreeMiddleLayout, "刹车功能", 140)

        self.__f_groupLayout(self.para.motorControlDic, tabThreeRightLayout, "运行电流环参数", 240)
        self.__f_groupLayout(self.para.motorControlDic, tabThreeRightLayout, "限流环", 240)

        btnProtectLayout = QHBoxLayout()
        btnProtectLayout.addWidget(self.btnMotorControlSelectedWrite)
        btnProtectLayout.addWidget(self.btnMotorControlAllWrite)
        btnProtectLayout.addWidget(self.btnMotorControlAllRead)
        tabThreeRightLayout.addLayout(btnProtectLayout)

        tabThreeLeftLayout.addItem(spacerV)
        tabThreeMiddleLayout.addItem(spacerV)
        tabThreeRightLayout.addItem(spacerV)
        self.tabThreeLayout.addItem(spacerH)
        # </editor-fold>

        # <editor-fold desc="电机保护参数布局">
        self.tabThreeLayout = QHBoxLayout()
        tabThreeLeftLayout = QVBoxLayout()
        tabThreeMiddleLayout = QVBoxLayout()

        self.tabThreeLayout.addLayout(tabThreeLeftLayout)
        self.tabThreeLayout.addLayout(tabThreeMiddleLayout)

        self.tabThree.setLayout(self.tabThreeLayout)

        self.__f_groupLayout(self.para.motorProtectDic, tabThreeLeftLayout, "硬件过流", 160)
        self.__f_groupLayout(self.para.motorProtectDic, tabThreeLeftLayout, "软件过流", 160)
        self.__f_groupLayout(self.para.motorProtectDic, tabThreeLeftLayout, "直流母线过欠压", 160)
        self.__f_groupLayout(self.para.motorProtectDic, tabThreeLeftLayout, "偏置电压保护", 160)

        self.__f_groupLayout(self.para.motorProtectDic, tabThreeMiddleLayout, "缺相保护", 140)
        self.__f_groupLayout(self.para.motorProtectDic, tabThreeMiddleLayout, "堵转保护", 140)
        self.__f_groupLayout(self.para.motorProtectDic, tabThreeMiddleLayout, "温度保护", 140)

        btnProtectLayout = QHBoxLayout()
        btnProtectLayout.addWidget(self.btnMotorProtectSelectedWrite)
        btnProtectLayout.addWidget(self.btnMotorProtectAllWrite)
        btnProtectLayout.addWidget(self.btnMotorProtectAllRead)
        tabThreeMiddleLayout.addLayout(btnProtectLayout)

        tabThreeLeftLayout.addItem(spacerV)
        tabThreeMiddleLayout.addItem(spacerV)

        self.tabThreeLayout.addItem(spacerH)
        # </editor-fold>

        # 设置总布局
        self.paraSetTab.setLayout(self.mainLayout)

    # 电机本体参数布局函数
    def __f_bodyParaLayout(self):
        """
        function:  电机本体参数布局函数
              in:  None
             out:  None
          return:  None
          others:  Motor Body Parameter Layout Func
        """
        bodyParaLayout = QVBoxLayout()
        qrb = QGroupBox("电机本体参数")
        bodyParaLayout.addWidget(qrb)
        grpLayout = QVBoxLayout()
        topLayout = QHBoxLayout()
        underLayout = QHBoxLayout()
        ckbLayout = QVBoxLayout()
        txtLayout = QVBoxLayout()
        lblLayout = QVBoxLayout()
        readLayout = QVBoxLayout()
        writeLayout = QVBoxLayout()
        topLayout.addLayout(ckbLayout)
        topLayout.addLayout(txtLayout)
        topLayout.addLayout(lblLayout)
        topLayout.addLayout(readLayout)
        topLayout.addLayout(writeLayout)
        spacerH = QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)
        spacerV = QSpacerItem(20, 20, QSizePolicy.Preferred, QSizePolicy.Expanding)
        topLayout.addItem(spacerH)
        grpLayout.addLayout(topLayout)
        grpLayout.addLayout(underLayout)
        grpLayout.addItem(spacerV)
        qrb.setLayout(grpLayout)
        self.tabOne.setLayout(bodyParaLayout)
        for k, v in self.para.motorPraDic.items():
            ckb = QCheckBox(k)
            ckb.setObjectName(k)
            ckb.setFixedSize(120, 25)
            ckbLayout.addWidget(ckb)
            self.para.motorPraDic[k]["ckb"] = ckb
            txt = QLineEdit()
            txt.setFixedSize(80, 25)
            txt.setObjectName(k)
            txtLayout.addWidget(txt)
            self.para.motorPraDic[k]["txt"] = txt
            lbl = QLabel(self.para.motorPraDic[k]["unit"])
            lbl.setFixedSize(30, 25)
            lblLayout.addWidget(lbl)
            btnRead = QPushButton("读取")
            btnRead.setFixedSize(60, 25)
            btnRead.clicked.connect(lambda k=k, Name=k, t=txt: self.__f_btnReadBodyParaClicked(Name, t))
            readLayout.addWidget(btnRead)
            self.para.motorPraDic[k]["read"] = btnRead
            btnWrite = QPushButton("写入")
            btnWrite.setFixedSize(60, 25)
            btnWrite.clicked.connect(lambda k=k, Name=k, t=txt: self.__f_btnWriteBodyParaClicked(Name, t))
            writeLayout.addWidget(btnWrite)
            self.para.motorPraDic[k]["write"] = btnWrite
        underLayout.addWidget(self.btnMotorPraSelectedWrite)
        underLayout.addWidget(self.btnMotorPraAllWrite)
        underLayout.addWidget(self.btnMotorPraAllRead)
        underLayout.addItem(spacerH)

    # GroupBox布局函数
    def __f_groupLayout(self, dic, layout, key, ckbWidth):
        """
        function:  GroupBox布局函数
              in:  dic：参数字典；
                   layout：GroupBox需要添加到的布局；
                   key：字典的Key值；
                   ckbWidth：checkBox的宽度；
             out:  None
          return:  None
          others:  GroupBox Layout Func
        """
        bodyLayout = QVBoxLayout()
        # V2.0.0 引入ctQGroupBox 结合滚动条的组合框控件
        qrb = ctQGroupBox(key)
        bodyLayout.addWidget(qrb)
        # 控件布局
        widgetLayout = QHBoxLayout()

        ckbLayout = QVBoxLayout()
        txtLayout = QVBoxLayout()
        lblLayout = QVBoxLayout()
        readLayout = QVBoxLayout()
        writeLayout = QVBoxLayout()

        widgetLayout.addLayout(ckbLayout)
        widgetLayout.addLayout(txtLayout)
        widgetLayout.addLayout(lblLayout)
        widgetLayout.addLayout(readLayout)
        widgetLayout.addLayout(writeLayout)
        # V2.0.0 水平方向策略
        spacerHG = QSpacerItem(1, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)
        widgetLayout.addItem(spacerHG)
        # 获取 ctQGroupBox 的内部布局
        grpInnerLayout = qrb.innerLayout
        # ctQGroupBox 的内部布局 添加控件布局
        grpInnerLayout.addLayout(widgetLayout)

        dicPara = dic[key]
        for k, v in dicPara.items():
            ckb = QCheckBox(k)
            ckb.setObjectName(k)
            ckb.setFixedSize(ckbWidth, 25)
            ckbLayout.addWidget(ckb)
            dic[key][k]["ckb"] = ckb
            txt = QLineEdit()
            txt.setFixedSize(80, 25)
            txt.setObjectName(k)
            txtLayout.addWidget(txt)
            dic[key][k]["txt"] = txt
            if dic[key][k]["unit"] is not None:
                lbl = QLabel(dic[key][k]["unit"])
            else:
                lbl = QLabel(" ")
            lbl.setFixedSize(30, 25)
            lblLayout.addWidget(lbl)
            btnRead = QPushButton("读取")
            btnRead.setFixedSize(40, 25)
            btnRead.clicked.connect(lambda paraName=k, paraTxt=txt, paraClass=key, paraDic=dic:
                                    self.__f_btnReadClicked(paraTxt, paraName, paraClass, paraDic))
            readLayout.addWidget(btnRead)
            dic[key][k]["read"] = btnRead
            btnWrite = QPushButton("写入")
            btnWrite.setFixedSize(40, 25)
            btnWrite.clicked.connect(lambda paraName=k, paraTxt=txt, paraClass=key, paraDic=dic:
                                     self.__f_btnWriteClicked(paraTxt, paraName, paraClass, paraDic))
            writeLayout.addWidget(btnWrite)
            dic[key][k]["write"] = btnWrite
        layout.addLayout(bodyLayout)

    # </editor-fold>

    # <editor-fold desc="按键响应函数">
    # 读取按键响应函数
    def __f_btnReadClicked(self, paraTxt, paraName, paraClass, paraDic):
        """
        function:  读取按键响应函数
              in:  paraTxt：文本框；
                   paraName：需要读取的参数名称；
                   paraClass：参数的名称分类：如硬件过流、软件过流；
                   paraDic：参数类中的具体某个参数字典，如：showPraDic、motorPraDic；
             out:  None
          return:  None
          others:  Read Button Response Func
        """
        # 判断串口是否打开
        if self.rtu.f_readComIsOpen() == 2:
            if self.control.isPoll:
                # V2.0.0要增加自动判断轮询是否启动的功能
                self.control.f_pollStopFunc()
                # QMessageBox.information(self.paraSetTab, '提示', '轮询尚未停止！', QMessageBox.Yes)
                # 这个地方要加上0.5s的延迟，不然轮询的报文和查询的报文会冲突
                time.sleep(0.5)
                # 清空txt
                paraTxt.clear()
                add = paraDic[paraClass][paraName]["add"]
                zoom = paraDic[paraClass][paraName]["zoom"]
                ir = self.rtu.f_modbusExecute(1, 3, add, 1)
                # 判断收发是否正确
                if ir["resMode"] == 0:
                    data = ir["data"]
                    data = data / zoom
                    data = self.__f_removeExtraZero(data)
                    paraTxt.setText(str(data))
                # 无论收发是否正确，都要开启轮询
                self.control.f_pollStartFunc()
            else:
                # 清空txt
                paraTxt.clear()
                add = paraDic[paraClass][paraName]["add"]
                zoom = paraDic[paraClass][paraName]["zoom"]
                ir = self.rtu.f_modbusExecute(1, 3, add, 1)
                # 判断收发是否正确
                if ir["resMode"] == 0:
                    data = ir["data"]
                    data = data / zoom
                    data = self.__f_removeExtraZero(data)
                    paraTxt.setText(str(data))

    # 写入按键响应函数
    def __f_btnWriteClicked(self, paraTxt, paraName, paraClass, paraDic):
        """
        function:  写入按键响应函数
              in:  paraTxt：文本框；
                   paraName：需要读取的参数名称；
                   paraClass：参数的名称分类：如硬件过流、软件过流；
                   paraDic：参数类中的具体某个参数字典，如：showPraDic、motorPraDic；
             out:  None
          return:  None
          others:  Writes Button Response Func
        """
        # 判断串口是否打开
        if self.rtu.f_readComIsOpen() == 2:
            if self.control.isPoll:
                # V2.0.0要增加自动判断轮询是否启动的功能
                self.control.f_pollStopFunc()
                # QMessageBox.information(self.paraSetTab, '提示', '轮询尚未停止！', QMessageBox.Yes)
                # 这个地方要加上0.5s的延迟，不然轮询的报文和查询的报文会冲突
                time.sleep(0.5)

                data = paraTxt.text()
                length = len(data)
                add = paraDic[paraClass][paraName]["add"]
                zoom = paraDic[paraClass][paraName]["zoom"]
                if length > 0:
                    data = float(data) * zoom
                    ir = self.rtu.f_modbusExecute(1, 6, add, data)
                    # 判断收发是否正确
                    if ir["resMode"] == 1:
                        QMessageBox.information(self.paraSetTab, "提示", f"{paraName}写入成功")
                # 无论收发是否正确，都要开启轮询
                self.control.f_pollStartFunc()
            else:
                data = paraTxt.text()
                length = len(data)
                add = paraDic[paraClass][paraName]["add"]
                zoom = paraDic[paraClass][paraName]["zoom"]
                if length > 0:
                    data = float(data) * zoom
                    ir = self.rtu.f_modbusExecute(1, 6, add, data)
                    # 判断收发是否正确
                    if ir["resMode"] == 1:
                        QMessageBox.information(self.paraSetTab, "提示", f"{paraName}写入成功")

    # 读取电机本体参数按键响应函数
    def __f_btnReadBodyParaClicked(self, paraName, paraTxt):
        """
        function:  读取电机本体参数按键响应函数
              in:  paraName:需要读取的参数名称；
                   paraTxt:文本框；
             out:  None
          return:  None
          others:  Read Motor Body Parameters Button Response Func
        """
        # 判断串口是否打开
        if self.rtu.f_readComIsOpen() == 2:
            if self.control.isPoll:
                # V2.0.0要增加自动判断轮询是否启动的功能
                self.control.f_pollStopFunc()
                # QMessageBox.information(self.paraSetTab, '提示', '轮询尚未停止！', QMessageBox.Yes)
                # 这个地方要加上0.5s的延迟，不然轮询的报文和查询的报文会冲突
                time.sleep(0.5)

                # 清空txt
                paraTxt.clear()
                add = self.para.motorPraDic[paraName]["add"]
                zoom = self.para.motorPraDic[paraName]["zoom"]
                ir = self.rtu.f_modbusExecute(1, 3, add, 1)
                # 判断收发是否正确
                if ir["resMode"] == 0:
                    data = ir["data"]
                    data = data / zoom
                    data = self.__f_removeExtraZero(data)
                    paraTxt.setText(str(data))

                # 无论收发是否正确，都要开启轮询
                self.control.f_pollStartFunc()
            else:
                # 清空txt
                paraTxt.clear()
                add = self.para.motorPraDic[paraName]["add"]
                zoom = self.para.motorPraDic[paraName]["zoom"]
                ir = self.rtu.f_modbusExecute(1, 3, add, 1)
                # 判断收发是否正确
                if ir["resMode"] == 0:
                    data = ir["data"]
                    data = data / zoom
                    data = self.__f_removeExtraZero(data)
                    paraTxt.setText(str(data))

    # 写入电机本体参数按键响应函数
    def __f_btnWriteBodyParaClicked(self, paraName, paraTxt):
        """
        function:  写入电机本体参数按键响应函数
              in:  paraName:需要读取的参数名称；
                   paraTxt:文本框；
             out:  None
          return:  None
          others:  Write Motor Body Parameters Button Response Func
        """
        # 判断串口是否打开
        if self.rtu.f_readComIsOpen() == 2:
            if self.control.isPoll:
                # V2.0.0要增加自动判断轮询是否启动的功能
                self.control.f_pollStopFunc()
                # QMessageBox.information(self.paraSetTab, '提示', '轮询尚未停止！', QMessageBox.Yes)
                # 这个地方要加上0.5s的延迟，不然轮询的报文和查询的报文会冲突
                time.sleep(0.5)

                data = paraTxt.text()
                add = self.para.motorPraDic[paraName]["add"]
                zoom = self.para.motorPraDic[paraName]["zoom"]
                length = len(data)
                if length > 0:
                    data = float(data) * zoom
                    ir = self.rtu.f_modbusExecute(1, 6, add, data)
                    # 判断收发是否正确
                    if ir["resMode"] == 1:
                        QMessageBox.information(self.paraSetTab, "提示", f"{paraName}写入成功")

                # 无论收发是否正确，都要开启轮询
                self.control.f_pollStartFunc()
            else:
                data = paraTxt.text()
                add = self.para.motorPraDic[paraName]["add"]
                zoom = self.para.motorPraDic[paraName]["zoom"]
                length = len(data)
                if length > 0:
                    data = float(data) * zoom
                    ir = self.rtu.f_modbusExecute(1, 6, add, data)
                    # 判断收发是否正确
                    if ir["resMode"] == 1:
                        QMessageBox.information(self.paraSetTab, "提示", f"{paraName}写入成功")

    # 多个参数读取写入按键响应函数
    def __f_multiBtnRWClicked(self, isSelected, isWrite, isBody, paraDic):
        """
        function:  多个参数读取写入按键响应函数
              in:  isSelected:是否是选中，如果是为True，不是为False；
                   isWrite：模式，True为写入，False为读取；
                   isBody：是否为本体参数，True为本体，False为控制或保护参数；
                   paraDic：传入的参数字典；
             out:  None
          return:  None
          others:  Multiple Parameters Read Write Button Response Func
        """
        # 判断串口是否打开
        if self.rtu.f_readComIsOpen() == 2:
            if self.control.isPoll:
                QMessageBox.information(self.paraSetTab, '提示', '轮询尚未停止！', QMessageBox.Yes)
            else:
                # 多个参数读写的字典 先清空
                self.dic = {}
                # 写入
                if isWrite:
                    mode = 6
                # 读取
                else:
                    mode = 3
                # 判断是否是本体参数
                if isBody:
                    for k, v in paraDic.items():
                        add = v["add"]
                        zoom = v["zoom"]
                        txt = v["txt"]
                        unit = v["unit"]
                        if unit is None:
                            unit = ""
                        value = txt.text()
                        # 如果是写入
                        if isWrite:
                            # 判断写入的数据是否正确
                            if len(value) > 0:
                                value = float(value) * zoom
                            else:
                                break
                        # 如果是读取
                        else:
                            # 读取的地址数量
                            value = 1
                        # 如果是选中写入
                        if isSelected:
                            # 判断checkBox是否选中
                            if v["ckb"].isChecked():
                                self.dic[f"{k}"] = {"add": add, "zoom": zoom, "txt": txt, "value": value, "mode": mode}
                        # 如果是全部写入和读取
                        else:
                            self.dic[f"{k}"] = {"add": add, "zoom": zoom,
                                                "txt": txt, "value": value,
                                                "mode": mode, "unit": unit}
                        if mode == 3:
                            txt.clear()
                # 不是本体参数，是控制和保护参数
                else:
                    for k, v in paraDic.items():
                        for k1, v1 in v.items():
                            add = v1["add"]
                            zoom = v1["zoom"]
                            txt = v1["txt"]
                            unit = v1["unit"]
                            if unit is None:
                                unit = ""
                            value = txt.text()
                            # 如果是写入
                            if isWrite:
                                # 判断写入的数据是否正确
                                if len(value) > 0:
                                    value = float(value) * zoom
                                else:
                                    break
                            # 如果是读取
                            else:
                                value = 1
                            if isSelected:
                                # 判断checkBox是否选中
                                if v1["ckb"].isChecked():
                                    self.dic[f"{k1}"] = {"add": add, "zoom": zoom, "txt": txt, "value": value,
                                                         "mode": mode}
                                # 如果是全部写入和读取
                            else:
                                self.dic[f"{k}{k1}"] = {"add": add, "zoom": zoom, "txt": txt,
                                                        "value": value, "mode": mode, "unit": unit}
                            if mode == 3:
                                txt.clear()
                if len(self.dic) > 0:
                    self.multiThread.dic = self.dic
                    self.multiThread.t_listShow.clear()
                    self.multiThread.start()
                    self.multiThread.isRunning = True
                    # 调用按键使能设置函数
                    self.__f_btnSetEnabled(False)

    # 一键读写按键点击响应函数
    def __f_btnAllRWClicked(self, isWrite):
        """
        function:  一键读写按键点击响应函数
              in:  isWrite：模式，True为写入，False为读取；
             out:  None
          return:  None
          others:  One-click Read/Write Button Clicked The Response Func
        """
        if self.rtu.f_readComIsOpen() == 2:
            # 判断是否正在轮询
            if self.control.isPoll:
                QMessageBox.information(self.paraSetTab, '提示', '轮询尚未停止！', QMessageBox.Yes)

                # </editor-fold>
            else:
                dic = {}
                # 写入
                if isWrite:
                    mode = 6
                    dialogTxt = "参数写入进度"
                # 读取
                else:
                    mode = 3
                    dialogTxt = "参数读取进度"
                length = 0
                # 本体参数
                for k, v in self.para.motorPraDic.items():
                    length += 1
                    add = v["add"]
                    zoom = v["zoom"]
                    txt = v["txt"]
                    unit = v["unit"]
                    if unit is None:
                        unit = ""
                    value = txt.text()
                    # 如果是写入
                    if isWrite:
                        # 判断写入的数据是否正确
                        if len(value) > 0:
                            value = float(value) * zoom
                        else:
                            break
                    # 如果是读取
                    else:
                        value = 1
                    dic[f"{k}"] = {"add": add, "zoom": zoom,
                                   "txt": txt, "value": value,
                                   "mode": mode, "unit": unit}
                    if mode == 3:
                        txt.clear()
                # 控制参数
                for k, v in self.para.motorControlDic.items():
                    for k1, v1 in v.items():
                        length += 1
                        addControl = v1["add"]
                        zoomControl = v1["zoom"]
                        txtControl = v1["txt"]
                        unitControl = v1["unit"]
                        if unitControl is None:
                            unitControl = ""
                        valueControl = txtControl.text()
                        # 如果是写入
                        if isWrite:
                            # 判断写入的数据是否正确
                            if len(valueControl) > 0:
                                if f_isHexString(valueControl):
                                    valueControl = int(valueControl, 16)
                                valueControl = float(valueControl) * zoomControl
                            else:
                                break
                        # 如果是读取
                        else:
                            valueControl = 1
                        dic[f"{k}{k1}"] = {"add": addControl, "zoom": zoomControl, "txt": txtControl,
                                           "value": valueControl, "mode": mode, "unit": unitControl}
                        if mode == 3:
                            txtControl.clear()
                # 保护参数
                for k, v in self.para.motorProtectDic.items():
                    for k1, v1 in v.items():
                        length += 1
                        addProtect = v1["add"]
                        zoomProtect = v1["zoom"]
                        txtProtect = v1["txt"]
                        unitProtect = v1["unit"]
                        if unitProtect is None:
                            unitProtect = ""
                        valueProtect = txtProtect.text()
                        # 如果是写入
                        if isWrite:
                            # 判断写入的数据是否正确
                            if len(valueProtect) > 0:
                                valueProtect = float(valueProtect) * zoomProtect
                            else:
                                break
                        # 如果是读取
                        else:
                            valueProtect = 1

                        dic[f"{k}{k1}"] = {"add": addProtect, "zoom": zoomProtect, "txt": txtProtect,
                                           "value": valueProtect, "mode": mode, "unit": unitProtect}
                        if mode == 3:
                            txtProtect.clear()
                # <editor-fold desc="线程开启">
                if len(dic) == length:
                    self.multiThread.dic = dic
                    self.multiThread.t_listShow.clear()
                    self.multiThread.start()
                    self.multiThread.isRunning = True
                    isShowDialog = True
                    self.multiThread.isShowDialog = isShowDialog
                    # 调用按键使能设置函数
                    self.__f_btnSetEnabled(False)
                    # <editor-fold desc="进度条">
                    self.pgDialog = QProgressDialog(minimum=0, maximum=100, parent=self.paraSetTab,
                                                    labelText=f"{dialogTxt}")
                    # 隐藏进度条对话框上的“最大化”、“最小化”、“关闭”
                    self.pgDialog.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint)

                    # 设置标题
                    self.pgDialog.setWindowTitle("提示")
                    # 删除取消按键
                    # self.pgDialog.setCancelButton(None)

                    # V2.0.0版本要求增加关闭按键
                    self.pgDialog.canceled.connect(self.__f_dialogCanceled)

                    self.pgDialog.resize(400, 100)
                    self.pgDialog.setWindowModality(Qt.ApplicationModal)
                    self.pgDialog.show()

                    # </editor-fold>
                # </editor-fold>

    # 参数导出按键点击响应函数
    def __f_btnExportClicked(self):
        """
        function:  参数导出按键点击响应函数
              in:  None
             out:  None
          return:  None
          others:  Parameter export key click function
        """
        # 将所有参数名称存入到列表中
        exportNameLst = []
        # 将所有参数存入到列表中来
        exportParaLst = []
        for key, value in self.para.motorPraDic.items():
            exportNameLst.append(key)
            exportParaLst.append(self.para.motorPraDic[key]["txt"].text())
        for key, value in self.para.motorControlDic.items():
            for k, v in self.para.motorControlDic[key].items():
                exportNameLst.append(f"{key}-{k}")
                exportParaLst.append(v["txt"].text())
        for key, value in self.para.motorProtectDic.items():
            for k, v in self.para.motorProtectDic[key].items():
                exportNameLst.append(f"{key}-{k}")
                exportParaLst.append(v["txt"].text())
        # 参数个数
        length = len(exportParaLst)
        # 转换成列表
        numLst = range(0, length)
        # 获取当前文件所在文件夹路径
        folder_path = os.getcwd()
        # 测试结果存放路径
        save_path_dir = f"{folder_path}\\MotorPara"
        # 判断该路径是否存在
        if not os.path.exists(save_path_dir):
            # 如果不存在则创建
            os.makedirs(save_path_dir)
        # 获取当前时间
        now_time = datetime.now()
        # 设置时间输出格式
        new_time = now_time.strftime('[%Y-%m-%d %H-%M-%S]')
        # 增加输入提示框，可以自定义导出文件名称
        file_name, ok = QInputDialog.getText(self.paraSetTab, "提示", "请输入导出文件名称:", QLineEdit.Normal,
                                             f"{new_time}电机参数导出.xlsx")
        # 如果用户确认
        if ok:
            # 判断是否含有.xlsx后缀，没有添加
            if file_name.find(".xlsx") == -1:
                file_name += ".xlsx"
            # 创建excel文件的路径
            file_path = f"{save_path_dir}\\{file_name}"
            # 表头
            headLst = ['序号', '参数名称', "数据"]
            # 要写入的数据
            data = [numLst, exportNameLst, exportParaLst]
            tExcel = tExcelHelper.ctExcel()
            isTrue = tExcel.f_createExcel(headLst, data, file_path)
            # <editor-fold desc="导出成功判断">
            if os.path.exists(file_path) and isTrue:  # 判断文件是否保存成功
                QMessageBox.information(self.paraSetTab, '提示', '参数导出成功!', QMessageBox.Yes)
            # </editor-fold>

    # 参数导入按键点击函数
    def __f_btnImportClicked(self):
        """
        function: 参数导入按键点击函数
              in: None
             out: None
          return: None
          others: Parameter import key click function
        """
        # 获取当前文件所在文件夹路径
        folder_path = os.getcwd()
        # 打开文件路径
        open_path_dir = f"{folder_path}\\MotorPara"
        # 设置文件选择框格式
        filepath = QFileDialog.getOpenFileName(self.paraSetTab, "选择需要导入的文件", open_path_dir,
                                               "Excel(*.xlsx);;Excel(*.xls)")
        if filepath[0] != "":
            tExcel = tExcelHelper.ctExcel()
            getData = tExcel.f_readExcel(filepath[0])
            # 将两个列表内容合并成一个字典
            dataDic = dict(zip(getData[0], getData[1]))
            # 写一个 try 把可能出错的代码放进去。
            try:
                for key, value in self.para.motorPraDic.items():
                    txt = self.para.motorPraDic[key]["txt"]
                    data = dataDic[key]
                    txt.setText(str(data))
                for key, value in self.para.motorControlDic.items():
                    for k, v in self.para.motorControlDic[key].items():
                        txt = v["txt"]
                        data = dataDic[f"{key}-{k}"]
                        txt.setText(str(data))
                for key, value in self.para.motorProtectDic.items():
                    for k, v in self.para.motorProtectDic[key].items():
                        txt = v["txt"]
                        data = dataDic[f"{key}-{k}"]
                        txt.setText(str(data))
                QMessageBox.information(self.paraSetTab, '提示', '参数导入成功!', QMessageBox.Yes)
            # 写一个except
            except Exception as e:
                QMessageBox.warning(self.paraSetTab, '提示', '导入时存在异常的数据', QMessageBox.Close)

    # </editor-fold>

    # <editor-fold desc="更新UI">
    # 全部读取后发送信号更新UI
    def __f_readUpdateUI(self, k, dic):
        """
        function:  全部读取后发送信号更新UI
              in:  k：发送信号，传出的参数名称
                   dic：发送信号，传出的参数字典
             out:  None
          return:  None
          others:  Send Signal To Update UI After Reading All
        """
        data = dic[k]["data"]
        data = str(data)
        unit = dic[k]["unit"]
        dic[k]["txt"].setText(data)
        new_time = datetime.now().strftime('[%Y-%m-%d %H-%M-%S]')
        self.workbench.txtSysLog.appendPlainText(f"{new_time}  :  电机{k} --> {data} {unit}")

    # 全部写入后发送信号更新UI
    def __f_writeUpdateUI(self, dic):
        """
        function:  全部写入后发送信号更新UI
              in:  dic：发送信号，传出参数字典
             out:  None
          return:  None
          others:  Send Signal To Update The UI After All Is Written
        """
        num = 0
        length = len(dic)

        for k, v in dic.items():
            if v["succeed"]:
                num += 1
        # 判断
        if num == length:
            self.multiThread.stop()
            self.multiThread.isRunning = False
            QMessageBox.information(self.paraSetTab, "提示", f"写入成功")
            self.__f_btnSetEnabled(True)
        else:
            self.multiThread.stop()
            self.multiThread.isRunning = False
            QMessageBox.warning(self.paraSetTab, "提示", f"写入失败")
            self.__f_btnSetEnabled(True)

    # 进度条更新函数
    def __f_dialogChangedUI(self, length, num):
        """
        function:  进度条更新函数
              in:  length：需要读写的数量
                   num：进度值
             out:  None
          return:  None
          others:  Progress Bar Update Func
        """
        schedule = num / length
        schedule = round(schedule, 2) * 100
        if schedule == 100:
            self.pgDialog.close()
            self.pgDialog = None
            self.multiThread.isShowDialog = False
            # if self.control.isPoll:
            #     time.sleep(0.5)
            #     # 开启轮询
            #     self.control.f_pollStartFunc()
        elif schedule <= 100:
            self.pgDialog.setValue(schedule)

    # </editor-fold>

    # 进度条cancel响应函数
    def __f_dialogCanceled(self):
        """
        function:  进度条cancel响应函数
              in:  None
             out:  None
          return:  None
          others:  Progress Bar Cancel Response Func
        """
        self.multiThread.isShowDialog = False
        self.multiThread.stop()

        self.multiThread.isRunning = False
        self.pgDialog.close()
        self.pgDialog = None

        self.__f_btnSetEnabled(True)

        # if self.control.isPoll:
        #     time.sleep(0.5)
        #     # 开启轮询
        #     self.control.f_pollStartFunc()

    # 按键使能设置函数
    def __f_btnSetEnabled(self, mode):
        """
        function:  按键使能设置函数
              in:  mode：使能，True；禁用，False
             out:  None
          return:  None
          others:  Button Set Enabled Func
        """
        # 电机本体参数
        self.btnMotorPraSelectedWrite.setEnabled(mode)
        self.btnMotorPraAllWrite.setEnabled(mode)
        self.btnMotorPraAllRead.setEnabled(mode)
        # 电机控制参数
        self.btnMotorControlSelectedWrite.setEnabled(mode)
        self.btnMotorControlAllWrite.setEnabled(mode)
        self.btnMotorControlAllRead.setEnabled(mode)
        # 电机保护参数
        self.btnMotorProtectSelectedWrite.setEnabled(mode)
        self.btnMotorProtectAllWrite.setEnabled(mode)
        self.btnMotorProtectAllRead.setEnabled(mode)
        # <editor-fold desc="单个读取&写入按键禁用">
        for k, v in self.para.motorControlDic.items():
            for k1, v1 in self.para.motorControlDic[k].items():
                self.para.motorControlDic[k][k1]["read"].setEnabled(mode)
                self.para.motorControlDic[k][k1]["write"].setEnabled(mode)
        for k, v in self.para.motorProtectDic.items():
            for k1, v1 in self.para.motorProtectDic[k].items():
                self.para.motorProtectDic[k][k1]["read"].setEnabled(mode)
                self.para.motorProtectDic[k][k1]["write"].setEnabled(mode)
        for k, v in self.para.motorPraDic.items():
            self.para.motorPraDic[k]["read"].setEnabled(mode)
            self.para.motorPraDic[k]["write"].setEnabled(mode)
        # </editor-fold>
        # 一键操作按键
        self.btnAllWrite.setEnabled(mode)
        self.btnAllRead.setEnabled(mode)
        self.btnImport.setEnabled(mode)
        self.btnExport.setEnabled(mode)

    # <editor-fold desc="静态方法">
    # 去掉小数点后多余的0
    @staticmethod
    def __f_removeExtraZero(n):
        """
        function:  去掉小数点后多余的0
              in:  n:去除前的数
             out:  n:去除后的数
          return:  n:int
          others:  Remove Extra 0 After The Decimal Point
        """
        # 先去除多余的0
        n = n - 0
        # 转变为字符串类型
        n = str(n)
        # 判断n是否以’.0‘结尾
        if n.endswith('.0'):
            # 是则删除’.0‘  即用空白符取代
            n = n.replace('.0', '')
            # 转变回int类型
            n = int(n)
        return n
    # </editor-fold>


# 判断是否是十六进制的字符串函数
def f_isHexString(s):
    """
    function:  判断是否是十六进制的字符串函数
          in:  s:需要判断的字符串
         out:  None
      return:  bool,True or False
      others:  Judge Whether Or Not Hexadecimal String Func
    """
    s = s.lower()
    if s.isalnum() and not s.isdigit():
        return True
    else:
        return False
